package net.daoke.relation.service;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.daoke.relation.model.domain.NetworkRelation;
import net.daoke.relation.model.domain.NodeDomain;
import net.daoke.relation.model.request.MergeNodeVO;
import net.daoke.relation.repository.NodeRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@AllArgsConstructor
public class NodeService {

    private final NodeRepository nodeRepository;
    private final NetworkService networkService;

    public NodeDomain getNodeByIp(String ip) {
        return nodeRepository.getOneByIp(ip).orElse(null);
    }

    public NodeDomain getNodeByNo(String no) {
        return nodeRepository.getNodeDomainByNo(no).orElse(null);
    }

    /**
     * 合并节点<br>
     * 将指定的一组节点合并到目标节点中。<br>
     * 将多个节点合并为一个，默认使用第一个节点的属性作为合并后的节点属性，其余节点的属性将被忽略，目标节点为空时，将创建一个新的节点
     *
     * @param nodeNos  要合并的节点编号列表
     * @param targetNo 目标节点编号，即合并后保留的节点编号
     * @return 合并操作是否成功，成功返回 true，失败返回 false
     */
    @Transactional
    public NodeDomain mergeNode(MergeNodeVO mergeNodeVO) {
        if (mergeNodeVO.getNodeNos() == null || mergeNodeVO.getNodeNos().isEmpty() || mergeNodeVO.getNodeNos().getFirst().isEmpty()) {
            throw new IllegalArgumentException("节点编号列表不能为空");
        }
        // 获取源节点列表
        List<NodeDomain> sourceNodeDomainList = new ArrayList<>(mergeNodeVO.getNodeNos().stream().map(nodeNo -> {
            NodeDomain nodeDomain = getNodeByNo(nodeNo);
            if (nodeDomain == null) {
                throw new IllegalArgumentException("节点[" + nodeNo + "]不存在");
            }
            return nodeDomain;
        }).toList());
        // 获取目标节点列表的第一个节点，作为合并后的节点的属性来源
        NodeDomain firstNode = sourceNodeDomainList.getFirst();
        // 获取目标节点
        NodeDomain targetNode = getNodeByNo(mergeNodeVO.getTargetNo());
        if (targetNode == null) {
            // 如果目标节点为空，则创建一个新的节点
            targetNode = new NodeDomain();
            targetNode.setType(firstNode.getType());
            targetNode.setName(firstNode.getName());
            targetNode.setDescription(firstNode.getDescription());
            targetNode.setIps(firstNode.getIps());
            targetNode.setRegion(firstNode.getRegion());
            targetNode = nodeRepository.save(targetNode);
        } else {
            // 如果目标节点不为空，则更新目标节点的属性
            targetNode.setNo(mergeNodeVO.getTargetNo());
            targetNode.setType(firstNode.getType());
            targetNode.setName(firstNode.getName());
            targetNode.setDescription(firstNode.getDescription());
            targetNode.setIps(firstNode.getIps());
            targetNode.setRegion(firstNode.getRegion());
            targetNode = nodeRepository.save(targetNode);
        }
        // 获取源节点的关联关系列表，并更新关联关系的目标节点编号
        List<NetworkRelation> networkRelations = new ArrayList<>();
        NodeDomain finalTargetNode = targetNode;
        sourceNodeDomainList.forEach(nodeDomain -> {
            List<NetworkRelation> relationList = networkService.getNetworkRelationsByNodeNo(nodeDomain.getNo());
            // 调转关联关系的方向
            relationList.forEach(networkRelation -> {
                if (networkRelation.getDestinationNo().equals(nodeDomain.getNo())) {
                    networkRelation.setSourceNo(finalTargetNode.getNo());
                    networkRelation.setDestinationNo(nodeDomain.getNo());
                }
            });
            networkRelations.addAll(relationList);
        });
        // 更新关联关系的目标节点编号
        networkRelations.stream().distinct().forEach(networkRelation -> {
            NetworkRelation relation = networkService.updateNetworkRelation(
                    networkRelation.getSourceNo(),
                    networkRelation.getDestinationNo(),
                    finalTargetNode.getNo()
            );
        });
        // 移除目标节点编号，避免重复处理，避免将目标节点也加入到要删除的列表中
        sourceNodeDomainList.remove(targetNode);
        // 删除源节点
        nodeRepository.deleteAll(sourceNodeDomainList);
        return finalTargetNode;
    }
}
